package gc3d.griddata;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.Serializable;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;

import org.apache.commons.net.io.Util;

/**
 * @author Pierrick
 *
 */
public class GridPovData implements Serializable, Cloneable {
	private static final long serialVersionUID = 1L;
	private int width;
	private int height;
	private boolean aa;
	private double aat;
	private int aad;
	//private boolean cyclanim;
	private int totalframes;
	private int framenumber;
	private double clock_start;
	private double clock_end;
	private String povfilename;
	private String framenumberWZeros;
	private String filename;

	public GridPovData(int width, int height, int totalframes, 
			double clock_start, double clock_end, 
			boolean aa, double aat, int aad, 
			String filename, byte [] data) throws IOException {
		this.width = width;
		this.height = height;
		this.aa = aa;
		this.aat = aat;
		this.aad = aad;
		//this.cyclanim = cyclanim;
		this.totalframes = totalframes;
		this.clock_start = clock_start;
		this.clock_end   = clock_end;
		this.framenumber = 0;
		this.filename = filename;
		this.framenumberWZeros = "";

		this.povfilename = this.checkPovInZip(data);
	}

	/**
	 * test pov in the zip
	 * @throws IOException ZIP error, Directory in the ZIP, no .pov in the ZIP
	 */
	private String checkPovInZip(byte [] data) throws IOException {
		String povname = null;
		if (filename.substring( filename.lastIndexOf('.') ).equals(".zip")) {
			ZipEntry entry;
			ZipInputStream zis = new ZipInputStream(new ByteArrayInputStream(data));
			try {
				while(povname == null && (entry = zis.getNextEntry()) != null) {
					if (entry.isDirectory()) {
						throw new IOException("Do not put directory in the .zip archive");
					} else {
						String entryname = entry.getName();
						if ( entryname.substring( entryname.lastIndexOf('.') ).equals(".pov") ) {
							povname = entryname;
						}
					}
				}
			} finally {
				zis.close();
			}
			if (povname == null) {
				throw new IOException("No .pov file found in the .zip archive");
			}
		} else if (filename.substring( filename.lastIndexOf('.') ).equals(".pov")) {
			povname = this.filename;
		} else {
			throw new IOException("Just .zip or .pov supported");
		}
		return povname;
	}

	public GridPovData clone() {
		GridPovData cloneGridPovData = null;
		try {
			cloneGridPovData = (GridPovData) super.clone();
			cloneGridPovData.width       = this.width;
			cloneGridPovData.height      = this.height;
			cloneGridPovData.aa          = this.aa;
			cloneGridPovData.aat         = this.aat;
			cloneGridPovData.aad         = this.aad;
			cloneGridPovData.totalframes = this.totalframes;
			cloneGridPovData.clock_start = this.clock_start;
			cloneGridPovData.clock_end   = this.clock_end;
			cloneGridPovData.filename    = new String(this.filename);
			cloneGridPovData.povfilename = new String(this.povfilename);
		} catch (CloneNotSupportedException cnse) {
			cnse.printStackTrace();
		}
		return cloneGridPovData;
	}

	public String toString() {
		String libpath = System.getProperty("java.io.tmpdir") 
			+ File.separatorChar + "GridPov" 
			+ File.separatorChar + "include";
		StringBuffer buff = new StringBuffer();
		buff.append("Display=off\r\n");
		buff.append("Verbose=off\r\n");
		buff.append("All_Console=off\r\n");
		buff.append("Draw_Vistas=off\r\n");
		buff.append("Output_File_Type=N\r\n");
		buff.append("Pause_when_Done=off\r\n");

		buff.append("Input_File_Name="+povfilename+"\r\n");
		buff.append("Width="+width+"\r\n");
		buff.append("Height="+height+"\r\n");
		buff.append("Antialias="+(aa==true?"on":"off")+"\r\n");
		buff.append("Antialias_Threshold="+aat+"\r\n");
		buff.append("Antialias_Depth="+aad+"\r\n");
		//buff.append("Cyclic_Animation="+(cyclanim==true?"on":"off")+"\r\n");

		double clock = framenumber * (clock_end - clock_start) / totalframes;
		buff.append("Clock="+clock+"\r\n");
		buff.append("Output_File_Name="+getPNGName()+"\r\n");
		buff.append("Library_Path="+libpath+"\r\n");

		return buff.toString();
	}

	public void write(String dir, FileInputStream povfis) throws IOException {
		this.writePOV(dir, povfis);
		this.writeINI(dir);
	}

	private void writeINI(String dir) throws IOException {
		// INI
		FileOutputStream inifos = new FileOutputStream(dir 
				+ File.separatorChar + getININame());
		inifos.write(this.toString().getBytes());
		inifos.close();
	}

	private void writePOV(String dir, FileInputStream is) throws IOException {
		String libpath = System.getProperty("java.io.tmpdir") 
			+ File.separatorChar + "GridPov" 
			+ File.separatorChar + "include";
		File libdir = new File(libpath);

		if (!libdir.exists()) {
			libdir.mkdirs();
		}

		if (filename.substring( filename.lastIndexOf('.') ).equals(".zip")) {
			writePOVz(dir, libpath, is);
		}
	}

	private void writePOVz(String dir, String libpath, FileInputStream is) throws IOException {
		String destdir;
		ZipEntry entry;
		ZipInputStream zis = new ZipInputStream(is);

		try {
			while((entry = zis.getNextEntry()) != null) {
				String entryname = entry.getName();
				if ( entryname.substring( entryname.lastIndexOf('.') ).equals(".pov") ) {
					destdir = dir;
				} else {
					destdir = libpath;
				}

				File filefromzip = new File(destdir + File.separatorChar + entryname);
				if (!filefromzip.exists()) {
					System.out.println("[INFO ]["+this.getClass()+".writePOVz] Extracting: "+filefromzip.getPath());
					// write the files to the disk
					FileOutputStream fos = new FileOutputStream(filefromzip);
					try {
						Util.copyStream(zis, fos);
					} finally {
						fos.close();
					}
				}
			}
		} finally {
			zis.close();
		}
	}

	public void setFrameNumber(int framenumber) {
		if (framenumber>=0 && framenumber<=this.totalframes) {
			this.framenumber  = framenumber;

			// build the digit string (for video encoding)
			int totaldigits = 0;
			int framedigits = 0;
			String zeros = new String("");
			for (int tmp = this.totalframes; tmp > 0; tmp/=10, totaldigits++);
			if (framenumber == 0) {
				framedigits = 1;
			} else {
				for (int tmp = this.framenumber; tmp > 0; tmp/=10, framedigits++);
			}
			for (int tmp = 0; tmp < (totaldigits-framedigits); tmp++)
				zeros+="0";
			framenumberWZeros = zeros+framenumber;
		} else {
			//throw new OutOfRange();
			System.err.println(this+".setFrameNumber OutOfRange: "+framenumber);
		}
	}

	public String getFileName() {
		return filename;
	}

	public String getPNGName() {
		return povfilename + framenumberWZeros + ".png";
	}

	public String getININame() {
		return povfilename + framenumberWZeros + ".ini";
	}

	public int getTotalFrames() {
		return totalframes;
	}

	public int getFrameNumber() {
		return framenumber;
	}
}
